﻿using UnityEngine;
using System.Collections.Generic;
using UnityEditor;
using System.IO;
using System.Text.RegularExpressions;
using System.Text;
public class MethodNameModifier
{
    class PoolManager
    {
        static List<StringBuilder> _sbList;
        static public StringBuilder GetStringBuilder()
        {
            StringBuilder ret = null;
            if (null == _sbList || _sbList.Count == 0)
                ret = new StringBuilder(1000);
            else
            {
                ret = _sbList[_sbList.Count - 1];
                _sbList.RemoveAt(_sbList.Count - 1);
            }
            return ret;
        }
        static public void ReleaseStringBuilder(StringBuilder sb)
        {
            sb.Length = 0;
            if (null == _sbList)
                return;
            _sbList.Add(sb);
        }
        static public void ClearStringBuilder()
        {
            if (null != _sbList)
            {
                _sbList.Clear();
                _sbList = null;
            }
        }
    }
    class CharNode
    {
        static List<CharNode> _pool;
        static public CharNode GetFromPool()
        {
            CharNode ret;
            if (null == _pool || _pool.Count == 0)
                ret = new CharNode();
            else
            {
                ret = _pool[_pool.Count - 1];
                _pool.RemoveAt(_pool.Count - 1);
            }
            return ret;
        }
        static public void ReleaseToPool(CharNode node)
        {
            if (null == _pool)
                _pool = new List<CharNode>();
            _pool.Add(node);
        }
        static public void ClearPool()
        {
            if (null != _pool)
            {
                _pool.Clear();
                _pool = null;
            }
        }
        public char value { get; private set; }
        public string keyword { get; private set; }
        public bool isDirty { get { return !string.IsNullOrEmpty(keyword); } }
        public CharNode parent { get; private set; }
        public Dictionary<char, CharNode> _children;
        public int childCount { get { return null == _children ? 0 : _children.Count; } }

        protected void AddChild(string text, int index)
        {
            var key = text[index];

            CharNode node = null;

            if (null == _children)
                _children = new Dictionary<char, CharNode>();

            if (!_children.TryGetValue(key, out node))
            {
                node = GetFromPool();
                node.value = key;
                node.parent = this;
                _children[key] = node;
            }

            if (index == text.Length - 1)
                node.keyword = text;
            else
                node.AddChild(text, index + 1);
        }
        public CharNode GetChild(char c)
        {
            if (null == _children)
                return null;
            CharNode node;
            _children.TryGetValue(c, out node);
            return node;
        }
        public void AddKeyword(string text)
        {
            if (string.IsNullOrEmpty(text))
                return;
            this.AddChild(text, 0);
        }
        public void Clear()
        {
            value = '0';
            keyword = null;
            parent = null;
            if (null != _children)
            {
                foreach (var node in _children)
                    node.Value.Clear();
                _children.Clear();
            }
            ReleaseToPool(this);
        }
        public int FindNextMatchIndex(string text, int startIndex, out CharNode ret)
        {
            ret = null;
            int len = text.Length;

            CharNode node;
            for (int i = startIndex; i < len; ++i)
            {
                node = null;
                this.FindMatchChild(text, i, ref node);
                if (null != node)
                {
                    ret = node;
                    return i;
                }
            }
            return -1;
        }
        //找到最长的匹配
        protected void FindMatchChild(string text, int index, ref CharNode ret)
        {
            if (index >= text.Length)
                return;
            CharNode node = GetChild(text[index]);
            if (null == node)
                return;
            if (node.isDirty)
                ret = node;
            node.FindMatchChild(text, index + 1, ref ret);
        }
    }





    [MenuItem("Assets/BeginModifyMethodName")]
    static void BeginModify()
    {
        var p = AssetDatabase.GetAssetPath(Selection.activeObject);
        Execute(0, p);
    }



    //单词字符：字母、数字、下划线
    static bool IsWordChar(char c)
    {
        if (c == '_')
            return true;
        if (c >= '0' && c <= '9')
            return true;
        if (c >= 'A' && c <= 'Z')
            return true;
        if (c >= 'a' && c <= 'z')
            return true;
        return false;
    }
    //空白符
    static bool IsBlankChar(char c)
    {
        if (c == ' '
            || c == '\t'
            || c == '\v'
            || c == '\n'
            )
            return true;
        return false;
    }


    const string THIS = @"this.";
    static int THIS_LEN = THIS.Length;

    //仅支持void，其他语法太复杂无法保证：可能会参与其他操作符中（如+ - * / []）
    static readonly string[] ReturnTypes = new string[] {
            "void",
            //"int",
            //"bool",
        };

    static CharNode _trieTree;
    static Dictionary<string, string> _nameMap;
    static Dictionary<string, int> _nameDefineCountMap;
    static HashSet<string> _privateMethods;
    static CharNode _excludeMethodNameTrieTree;
    static int majiaId = 0;
    static string _codePath;
    static int _methodIndex = 0;

    static public void Execute(int majiaId, string path = null)
    {
        MethodNameModifier.majiaId = majiaId;
        _codePath = path;
        if (string.IsNullOrEmpty(_codePath))
            _codePath = Application.dataPath + "/Assets/Scripts/";
        _nameMap = new Dictionary<string, string>();
        _nameDefineCountMap = new Dictionary<string, int>();
        _privateMethods = new HashSet<string>();

        _excludeMethodNameTrieTree = CharNode.GetFromPool();

        _excludeMethodNameTrieTree.AddKeyword("Awake");
        _excludeMethodNameTrieTree.AddKeyword("Start");
        _excludeMethodNameTrieTree.AddKeyword("OnEnable");
        _excludeMethodNameTrieTree.AddKeyword("OnDisable");
        _excludeMethodNameTrieTree.AddKeyword("Update");
        _excludeMethodNameTrieTree.AddKeyword("FixedUpdate");
        _excludeMethodNameTrieTree.AddKeyword("LateUpdate");
        _excludeMethodNameTrieTree.AddKeyword("OnDestroy");

        _excludeMethodNameTrieTree.AddKeyword("OnApplicationFocus");
        _excludeMethodNameTrieTree.AddKeyword("OnApplicationPause");
        _excludeMethodNameTrieTree.AddKeyword("OnApplicationQuit");
        _excludeMethodNameTrieTree.AddKeyword("OnLevelWasLoaded");

        _excludeMethodNameTrieTree.AddKeyword("OnPreCull");
        _excludeMethodNameTrieTree.AddKeyword("OnBecameInvisible");
        _excludeMethodNameTrieTree.AddKeyword("OnBecameVisible");
        _excludeMethodNameTrieTree.AddKeyword("OnWillRenderObject");
        _excludeMethodNameTrieTree.AddKeyword("OnPreRender");//相机渲染前
        _excludeMethodNameTrieTree.AddKeyword("OnPostRender");//相机渲染后
        _excludeMethodNameTrieTree.AddKeyword("OnRenderObject");//所有场景渲染完
        _excludeMethodNameTrieTree.AddKeyword("OnRenderImage");//后处理
        _excludeMethodNameTrieTree.AddKeyword("OnGUI");
        _excludeMethodNameTrieTree.AddKeyword("OnDrawGizmos");

        _excludeMethodNameTrieTree.AddKeyword("OnCollisionEnter");
        _excludeMethodNameTrieTree.AddKeyword("OnCollisionStay");
        _excludeMethodNameTrieTree.AddKeyword("OnCollisionExit");
        _excludeMethodNameTrieTree.AddKeyword("OnTriggerEnter");
        _excludeMethodNameTrieTree.AddKeyword("OnTriggerStay");
        _excludeMethodNameTrieTree.AddKeyword("OnTriggerExit");

        _excludeMethodNameTrieTree.AddKeyword("OnMouseDown");
        _excludeMethodNameTrieTree.AddKeyword("OnMouseUp");
        _excludeMethodNameTrieTree.AddKeyword("OnMouseEnter");
        _excludeMethodNameTrieTree.AddKeyword("OnMouseExit");
        _excludeMethodNameTrieTree.AddKeyword("OnMouseOver");
        _excludeMethodNameTrieTree.AddKeyword("OnMouseDrag");

        _excludeMethodNameTrieTree.AddKeyword("OnAnimationEvent");



        if (AssetDatabase.IsValidFolder(_codePath))
        {
            var files = Directory.GetFiles(_codePath, @"*.cs", SearchOption.AllDirectories);
            foreach (var p in files)
            {
                ModifyFile(p);
            }
        }
        else
        {
            ModifyFile(_codePath);
        }
        _nameMap = null;
        _nameDefineCountMap = null;
        _privateMethods = null;

        _excludeMethodNameTrieTree.Clear();
        _excludeMethodNameTrieTree = null;


        PoolManager.ClearStringBuilder();
        CharNode.ClearPool();

    }
    static bool ModifyFile(string path)
    {
        var text = File.ReadAllText(path);
        if (text.Contains("MonoBehaviour")
            || text.Contains("Reflection")
            || text.Contains("interface")
            )
            return false;

        var CLASS = "class";

        int clsIndex = text.IndexOf(CLASS, 0);
        if (clsIndex == -1)
            return false;

        clsIndex = text.IndexOf(CLASS, clsIndex + CLASS.Length);
        if (clsIndex != -1)
            return false;

        StringReader reader = new StringReader(text);
        string line = null;

        _trieTree = CharNode.GetFromPool();

        while (null != (line = reader.ReadLine()))
        {
            string methodName = GetMethodDefineName(line);
            if (null != methodName)
            {
                bool isPrivate = true;
                if (line.Contains("public")
                    || line.Contains("protected")
                    || line.Contains("internal")
                )
                    isPrivate = false;
                if (isPrivate)
                    _privateMethods.Add(methodName);

                int count;
                _nameDefineCountMap.TryGetValue(methodName, out count);
                _nameDefineCountMap[methodName] = count + 1;
            }
        }
        reader.Close();
        reader.Dispose();

        _methodIndex = 0;
        foreach (var defineCoutPair in _nameDefineCountMap)
        {
            if (defineCoutPair.Value != 1)//仅定义一次的私有方法才改名：重载的不改
                continue;
            var methodName = defineCoutPair.Key;
            if (!_privateMethods.Contains(methodName))
                continue;
            CharNode node;
            if (_excludeMethodNameTrieTree.FindNextMatchIndex(methodName, 0, out node) >= 0
                )
                continue;

            var newName = GetNewMethodName(methodName, majiaId, _methodIndex++);// string.Format("local_{0}_{1}", majId, NUM++);

            _nameMap[methodName] = newName;
        }

        if (_nameMap.Count > 0)
        {
            foreach (var keyword in _nameMap)
                _trieTree.AddKeyword(keyword.Key);

            ChangeMethodName(path, text);
        }
        _trieTree.Clear();
        _trieTree = null;

        _nameMap.Clear();
        _nameDefineCountMap.Clear();
        _privateMethods.Clear();

        return true;
    }
    //=======================================================================================
    //对一行的文本分析，如果是函数定义：返回定义的函数名
    //=======================================================================================
    static string GetMethodDefineName(string str)
    {

        //Regex reg = new Regex(@"((?!(public|protected|internal)).)*[\s\S]+void[\s]+(\w)+[\s\S]*\([\s\S]*\)[\s\S]*");

        string name = null;

        string TypeName = null;
        int startIndex = -1;
        for (int i = 0; i < ReturnTypes.Length; ++i)
        {
            startIndex = str.IndexOf(ReturnTypes[i]);
            if (startIndex != -1)
            {
                TypeName = ReturnTypes[i];
                break;
            }
        }
        if (startIndex < 0)
            return name;


        startIndex += TypeName.Length;

        int endIndex = str.IndexOf("(", startIndex);
        if (endIndex < 0)
            return name;
        if (str.IndexOf(")", endIndex + 1) < 0)
            return name;

        if (str.Contains(";")
            || str.Contains("=")
            || str.Contains("delegate")
            || str.Contains("base")//继承
            || str.Contains(":")
            )
            return name;


        name = str.Substring(startIndex, endIndex - startIndex);
        name = name.Trim();
        return name;
    }
    //=======================================================================================
    //尝试对符合条件的方法名进行修改，同时满足以下几个条件才会修改：
    //1.文件中不包含interface并且只有一个类定义
    //2.类中不包含非反射
    //3.非内置事件回调:如MonoBehaviour的事件回调方法、动画事件OnAnimationEvent
    //4.不存在重载的私有方法
    //=======================================================================================
    static void ChangeMethodName(string path, string text)
    {
        StringBuilder sb = null;

        string oldMethodName;
        string newMethodName;

        int oldNameLength;// = oldMethodName.Length;
        int newNameLength;// = newMethodName.Length;
        int index = 0;
        int len = text.Length;
        bool needReplace;
        int sizeChange = 0;
        while (index < len)
        {
            CharNode node;
            index = _trieTree.FindNextMatchIndex(text, index, out node);
            if (-1 == index)
                break;
            oldMethodName = node.keyword;
            oldNameLength = oldMethodName.Length;

            newMethodName = _nameMap[oldMethodName];
            newNameLength = newMethodName.Length;

            needReplace = true;

            bool hasNonBlankChar = false;

            if (index > 0)//前面
            {
                bool needCheckThis = true;
                for (int i = 1; i <= THIS_LEN; ++i)//检测是否为this. 不是则不需要替换
                {
                    var beforeIndex = index - i;
                    if (beforeIndex < 0)
                        break;

                    char beforeChar = text[beforeIndex];
                    //需要替换
                    if (beforeChar == '('   //作为委托传递
                        //|| beforeChar == ')'
                        || beforeChar == '{'
                        || beforeChar == '}'
                        || beforeChar == '='
                        || beforeChar == ';'
                        || beforeChar == ','
                        || beforeChar == '!'
                        || beforeChar == '+'
                        || beforeChar == '-'
                        || beforeChar == '*'
                        || beforeChar == '/'
                        || IsBlankChar(beforeChar)
                        )
                    {
                        if (hasNonBlankChar)
                            needReplace = false;
                        break;
                    }
                    //不需替换
                    if (beforeChar == '<'
                        || beforeChar == '\''
                        || beforeChar == '\"'
                        )//模板实参
                    {
                        needReplace = false;
                        break;
                    }
                    hasNonBlankChar = true;
                    if (needCheckThis)    //this调用检测
                    {
                        if (beforeChar != THIS[THIS_LEN - i])//不与this.匹配
                        {
                            needReplace = false;
                            break;
                        }
                    }
                }
            }
            if (needReplace && index + oldNameLength < len)//后面字符
            {
                var tailIndex = index + oldNameLength;

                for (int i = 0; i < 10; ++i)
                {
                    if (i + tailIndex >= len)
                        break;
                    var rightChar = text[i + tailIndex];
                    //不需替换
                    if (IsWordChar(rightChar)
                        || rightChar == '>'
                        || rightChar == '\''
                        || rightChar == '\"'
                        )
                    {
                        needReplace = false;
                        break;
                    }
                    //需要替换
                    if (rightChar == '('//函数声明或者调用
                        || rightChar == ')'//作为委托传递给函数
                        || rightChar == '{'//作为委托传递给函数
                        || rightChar == '}'//作为委托传递给函数
                        || rightChar == ';'//作为委托
                        || rightChar == ','//作为委托传递给函数
                        || IsBlankChar(rightChar)
                        )
                        break;
                }
            }
            if (needReplace)
            {
                if (null == sb)
                {
                    sb = PoolManager.GetStringBuilder();
                    sb.Append(text);
                }
                var writeIndex = index + sizeChange;
                sb.Remove(writeIndex, oldNameLength);
                sb.Insert(writeIndex, newMethodName);
                sizeChange += newNameLength - oldNameLength;
            }
            index = index + oldNameLength;
        }
        if (null != sb)
        {
            //var tag = "Assets/";
            //int headIndex = path.IndexOf(tag);
            //var relativePath = headIndex == -1 ? path : path.Substring(headIndex + tag.Length);

            //var destPath = string.Format("CodeTemp/{0}", relativePath);

            var destPath = path;// string.Format("CodeTemp/{0}", relativePath);
            var destDir = Path.GetDirectoryName(destPath);

            if (!Directory.Exists(destDir))
                Directory.CreateDirectory(destDir);

            File.WriteAllText(destPath, sb.ToString());

            PoolManager.ReleaseStringBuilder(sb);
        }
    }
    //=======================================================================================
    //随机获得新名字
    //=======================================================================================
    const int lenLow = ('z' - 'a') + 1;
    const int lenUp = ('Z' - 'A') + 1;
    const int lenNum = 10;
    static readonly char[] OtherCharArray = new char[] { '_' };
    static string GetNewMethodName(string oldName, int majiaId, int methodIndex)
    {
        var sb = PoolManager.GetStringBuilder();

        var nameLength = UnityEngine.Random.Range(1, 5);

        int firstCharCount = lenLow + lenUp + OtherCharArray.Length;
        int totalCharCount = firstCharCount + lenNum;

        int gap = UnityEngine.Random.Range(0, 20);
        int charIndex = majiaId % firstCharCount;

        for (int i = 0; i < nameLength; ++i)
        {
            if (charIndex < lenLow)//小字母
                sb.Append((char)('a' + charIndex));
            else if (charIndex - lenLow < lenUp)//大字母
                sb.Append((char)('A' + charIndex - lenLow));
            else if (charIndex - lenLow - lenUp < OtherCharArray.Length)//其他：如_
                sb.Append(OtherCharArray[charIndex - lenLow - lenUp]);
            else if (charIndex - lenLow - lenUp - OtherCharArray.Length < lenNum)//数字
                sb.Append((char)('0' + charIndex - lenLow - lenUp - OtherCharArray.Length));

            charIndex += gap;
            charIndex %= totalCharCount;
        }

        sb.Append("_");
        sb.Append(methodIndex);

        var ret = sb.ToString();

        PoolManager.ReleaseStringBuilder(sb);

        return ret;
    }
}